class SocketHandler {
    key;
    socket;
    #reconnectNumber = 0;   //重连次数
    #reconnectTime = 5;   //每10秒一次
    #reconnectTimer = null;    //重连定时器
    #reconnectMaxNumber = 5;    //最大重连次数
    #ip;
    #port;
    #callback;  //消息处理后的回调
    constructor(ip, port, key) {
        this.#ip = ip
        this.#port = port
        this.key = key;
        this.#socketConnected();
    }

    /**
     * 连接socket
     */
    #socketConnected() {
        this.socket = new WebSocket('ws://' + this.#ip + ':' + this.#port);
        this.socket.onopen = () => {
            if (this.#reconnectTimer != null) {
                showSuccessNotify('第' + this.#reconnectNumber + '次重新连接成功');
                this.#reconnectNumber = 0;
                clearTimeout(this.#reconnectTimer);
                this.#reconnectTimer = null;
            }
            this.#message();
        };
        this.socket.onerror = this.#error;
        this.socket.onclose = this.#close;
    }

    /**
     * 发送消息
     * @param messageType
     * @param data
     * @param callback
     */
    send(messageType, data, callback = () => {}) {
        // 判断 Value 是否为对象，如果不是对象则尝试转换为对象
        if (typeof data !== 'object' || data === null) {
            try {
                data = JSON.parse(JSON.stringify(data));
                // 转换为简单对象（可能会丢失原生对象的一些属性和方法）
            } catch (error) {
                showErrorNotify('无法将非对象值转换为对象:', error);
                return;
            }
        }
        this.#callback = callback;
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(this.#makeData(messageType, data));
        }else{
            //TODO 如果下线了，那么应该等待重新连接，然后再发起消息
            showErrorNotify('socket已经下线了，请刷新页面重试');
        }
    }

    /**
     * 消息处理
     */
    #message() {
        this.socket.onmessage = (e) => {
            let res = JSON.parse(e.data);
            // 判断 Value 是否为对象，如果不是对象则尝试转换为对象
            if (typeof res !== 'object' || res === null) {
                showErrorNotify('数据类型不正确：' + res);
                return;
            }
            messageSingleton.getInstance().handler(res.messageType, res.data, this.#callback);
        }
    }

    /**
     * 错误处理
     */
    #error(){
        if (this.#reconnectNumber < this.#reconnectMaxNumber) {
            this.#reconnectNumber++;
            showWarningNotify("socket连接失败，正在尝试第" + this.#reconnectNumber + "次连接");
            this.#reconnectTimer = setTimeout(() => {
                this.#socketConnected()
            }, 1000 * this.#reconnectTime);
        } else {
            this.#reconnectTimer = null;
            showErrorNotify('重新连接失败，请稍后再试');
        }
    }

    /**
     * 断开连接
     * @param e
     */
    #close(e){
        switch (e.code) {
            case 1000:
                break;
            case 1006:
                showWarningNotify('与服务器断开连接');
                break;
            default :
                showErrorNotify('连接丢失了，错误的状态码: ' + e.code + '，请稍后再试');

        }
    }

    #makeData(messageType, data){
        let res = {
            messageType: messageType,
            data: data,
            timestamp: Math.floor(new Date().getTime() / 1000),
        };
        // console.log(this.#makeUrlString(res));
        res.sign = md5(this.#makeUrlString(res) + this.key).toString();
        // console.log(res.sign);
        return JSON.stringify(res);
    }

    #makeUrlString(data, prefix = '') {
        // 判断 Value 是否为对象，如果不是对象则尝试转换为对象
        if (typeof data !== 'object' || data === null) {
            try {
                data = JSON.parse(JSON.stringify(data));
                // 转换为简单对象（可能会丢失原生对象的一些属性和方法）
            } catch (error) {
                showErrorNotify('无法将非对象值转换为对象:', error); return;
            }
        }
        var url = '';
        data = this.#krsort(data);
        Object.keys(data).forEach((key) => {
            var value = data[key];
            if (value === null || value === '') return;
            if (typeof value === 'object') {
                url += this.#makeUrlString(value, prefix + key + '.');
            } else {
                url += prefix + key + '=' + value + '&';
            }
        });
        return !prefix ? url.slice(0, -1) : url;
    }

    #krsort(data){
        return Object.keys(data).sort().reverse().reduce((acc, key) => {
            if (typeof data[key] === 'object') {
                acc[key] = this.#krsort(data[key]);
            } else {
                acc[key] = data[key];
            }
            return acc;
        }, {});
    }
}



let socket;
function socketConnect(ip, port, key){
    socket = new SocketHandler(ip, port, key);
}
function socketSend(messageType, data, callback = () => {}){
    socket.send(messageType, data, callback);
}